package com.douya.middleware.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import com.douya.middleware.property.ElasticSearchProperties;
import com.douya.middleware.property.DouYaCacheProperties;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;

import javax.annotation.Resource;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * elasticsearch配置类
 *
 * @Author hcy
 * @Date 2022/11/28
 */
@Configuration
@Slf4j
@ConditionalOnProperty(name = "douya.middleware.elasticsearch.enable", havingValue = "true")
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {

    @Resource
    DouYaCacheProperties douYaCacheProperties;

    @Bean
    @Override
    public RestHighLevelClient elasticsearchClient() {
        ElasticSearchProperties elasticSearch = douYaCacheProperties.getElasticSearch();
        if (Objects.isNull(elasticSearch)) {
            return null;
        }
        String url = elasticSearch.getUrl();
        String port = elasticSearch.getPort();
        String username = elasticSearch.getUsername();
        String password = elasticSearch.getPassword();
        if (StringUtils.isBlank(url) || StringUtils.isBlank(port) || StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            log.error("elasticSearch 配置不全");
            return null;
        } else {
            //es验证账号密码
            CredentialsProvider provider = new BasicCredentialsProvider();
            //填写账号密码
            provider.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(username, password)
            );
            return new RestHighLevelClient(RestClient.builder(new HttpHost(url, Integer.parseInt(port), "http"))
                    .setHttpClientConfigCallback(httpClientBuilder -> {
                        httpClientBuilder.disableAuthCaching();
                        return httpClientBuilder.setDefaultCredentialsProvider(provider);
                    })
            );
        }
    }

    @Bean
    public ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(elasticsearchClient());
    }

    @Bean
    public ElasticsearchConverter elasticsearchEntityMapper() {
        MappingElasticsearchConverter elasticsearchConverter = new MappingElasticsearchConverter(
                elasticsearchMappingContext(), new DefaultConversionService());
        elasticsearchConverter.setConversions(elasticsearchCustomConversions());
        return elasticsearchConverter;
    }

    @Bean
    @Override
    @SuppressWarnings("all")
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        List<Converter> converters = Lists.newArrayList();
        converters.add(LocalDateTimeToLongConverter.INSTANCE);
        converters.add(LongToLocalDateTimeConverter.INSTANCE);
        converters.add(LocalDateTimeToStringConverter.INSTANCE);
        converters.add(StringToLocalDateTimeConverter.INSTANCE);
        return new ElasticsearchCustomConversions(converters);
    }

    @WritingConverter
    enum LocalDateTimeToStringConverter implements Converter<LocalDateTime, String> {
        INSTANCE;

        @Override
        public String convert(LocalDateTime dateTime) {
            return String.valueOf(
                    dateTime.toInstant(java.time.ZoneOffset.of("+8")).toEpochMilli()
            );
        }
    }

    @WritingConverter
    enum LocalDateTimeToLongConverter implements Converter<LocalDateTime, Long> {
        INSTANCE;

        @Override
        public Long convert(LocalDateTime dateTime) {
            return dateTime.toInstant(java.time.ZoneOffset.of("+8")).toEpochMilli();
        }
    }

    @ReadingConverter
    enum LongToLocalDateTimeConverter implements Converter<Long, LocalDateTime> {
        INSTANCE;

        @Override
        public LocalDateTime convert(Long timestamp) {
            return LocalDateTime.ofInstant(
                    java.time.Instant.ofEpochMilli(timestamp),
                    java.time.ZoneOffset.of("+8")
            );
        }
    }

    @ReadingConverter
    enum StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
        INSTANCE;

        @Override
        public LocalDateTime convert(String timestamp) {
            return LocalDateTime.ofInstant(
                    java.time.Instant.ofEpochMilli(Long.parseLong(timestamp)),
                    java.time.ZoneOffset.of("+8")
            );
        }
    }


}
